﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Modbus.Device;
using Modbus.Utility;
using ModbusTestTool;
using System.Xml.Serialization;
using System.Xml;


namespace modBusTest
{
    
    public class modbusmapOrig
    {
        public static int plcOffset = 1;
        

        //private List<mapFields> storage = new List<mapFields>();
        public BindingList<mapFields> storage;
        
        private optimizeRange optInputRegRead = new optimizeRange();
        private optimizeRange optHoldRegRead = new optimizeRange();
        private ushort[] holdingRegistersRead = new ushort[10001];
        private ushort[] inputRegistersRead = new ushort[10001];


        

        /// <summary>
        /// Handle to setup up modbus port
        /// </summary>
        public IModbusSerialMaster master { set; get; }
        /// <summary>
        /// slave id with the device you are trying to communicate with
        /// </summary>
        public byte slaveID { set; get; }
        /// <summary>
        /// Read data from file into internal database 
        /// Some issues with .net not being used
        /// </summary>
        /// <param name="fileName">Name of file to read</param>
        //public void readData(string fileName){
        //    storage = new List<mapFields>();
        
        //    using (StreamReader sr = new StreamReader(fileName))
        //    {
        //        string stringToParse;
        //        while((stringToParse = sr.ReadLine())!=null){
        //            string[] words = stringToParse.Split('\t');

        //            mapFields tempFile = new mapFields(words);
        //            storage.Add(tempFile);
        //        }
        //    }
        //    //populate the opitimize structure
        //    processHoldingRegister();
        //    processInputRegister();
            
        //    //solve for optimal number of modbus command
        //    optInputRegRead.solve();
        //    optHoldRegRead.solve();
        
        //}

        /// <summary>
        /// This is an altername method to read in data
        /// The .net version was having some problem with some things
        /// </summary>
        /// <param name="filename"></param>
        //public void readDataMethodMain(string filename)
        //{

        //    storage = new List<mapFields>();

        //    using (StreamReader sr = new StreamReader(filename))
        //    {
        //        string stringToParse = sr.ReadToEnd();

        //        List<string> items = new List<string>();

        //        StringBuilder tempString = new StringBuilder();
        //        for (int i = 0; i < stringToParse.Length-1; i++)
        //        {
        //            if (stringToParse[i] == '\r' && stringToParse[i + 1] == '\n')
        //            {
        //                items.Add(tempString.ToString());
        //                tempString.Clear();
        //                i++;
        //            }
        //            else
        //            {
        //                tempString.Append(stringToParse[i]);
        //            }
                    
        //        }

        //        foreach(string item in items)
        //        {
        //            stringToParse = item;
        //            string[] words = stringToParse.Split('\t');

        //            if (words.Length == 3)
        //            {
        //                try
        //                {
        //                    mapFields tempFile = new mapFields(words);

            
        //                    storage.Add(tempFile);
        //                }
        //                catch (Exception err)
        //                {
        //                    throw (new Exception(stringToParse + " is not correct",err));
        //                }
        //            }
        //            else
        //            {
        //                throw new Exception(stringToParse + "is not correct");
        //            }
        //        }
        //    }
        //    //populate the opitimize structure
        //    processHoldingRegister();
        //    processInputRegister();

        //    //solve for optimal number of modbus command
        //    optInputRegRead.solve();
        //    optHoldRegRead.solve();

        //}




        public void readDataXml(string filename)
        {
            storage = new BindingList<mapFields>();            
            List<inputDataXml> xmlData = new List<inputDataXml>();
            XmlSerializer xDecoder = new XmlSerializer(typeof(List<inputDataXml>));

            if (File.Exists(filename))
            {
                xmlData = (List<inputDataXml>) xDecoder.Deserialize(File.Open(filename,FileMode.Open,FileAccess.Read,FileShare.ReadWrite));
                foreach (inputDataXml item in xmlData)
                {
                    if (item.paramName != "Reserved" || item.paramName != "")
                    {
                        storage.Add(new mapFields(item.paramName, item.register, item.type));
                    }
                }
            }
            else
            {

                //this is there to provide an example case
                StreamWriter outHandle = new StreamWriter("example.xml");
                xmlData.Add(new inputDataXml());
                xmlData.Add(new inputDataXml());
                xmlData.Add(new inputDataXml());
                xmlData.Add(new inputDataXml());
                xDecoder.Serialize(outHandle, xmlData);

            }

            //populate the opitimize structure
            processHoldingRegister();
            processInputRegister();

            //solve for optimal number of modbus command
            optInputRegRead.solve();
            optHoldRegRead.solve();
            
        }
 
        /// <summary>
        /// Return mapField associated with parameter name
        /// </summary>
        /// <param name="paramName">Name of parameter</param>
        /// <returns>Mapfield structure contating detail about parameter</returns>
        public mapFields findParam(string paramName)
        {
            for (int i = 0; i < storage.Count; i++)
            {
                if(storage[i].paramName == paramName){
                    return storage[i];
                }
            }

            throw (new Exception("Param Does Not Exist"));
        }
        /// <summary>
        /// Return mapfield associated with the register
        /// </summary>
        /// <param name="register"></param>
        /// <returns></returns>
        public mapFields findParam(int register)
        {
            for (int i = 0; i < storage.Count; i++)
            {
                if (storage[i].register == register)
                {
                    return storage[i];
                }
            }

            throw (new Exception("Param Does Not Exist"));
        }
        /// <summary>
        /// exposes internal data structure for the modbus commands
        /// </summary>
        /// <returns>returns that internal structure</returns>
        public BindingList <mapFields> getList()
        {
            return storage;
        }


        /// <summary>
        /// Writes all registers marked dirty via modbus
        /// </summary>
        public void writeDirty()
        {
            optimizeRange InputWrite=new optimizeRange();
            optimizeRange HoldingWrite=new optimizeRange();
            ushort[] holdingRegistersWrite = new ushort[10001];
            ushort[] inputRegistersWrite = new ushort[10001];
 
            var items = from n in storage where (n.dirty == true) && (n.registerType == mapFields.regType.holding) && n.parameterType == paramType.word_t select n;

            foreach (mapFields item in items)
            {
                HoldingWrite.addRegister((ushort)(item.baseRegister));
                holdingRegistersWrite[item.baseRegister] = ushort.Parse(item.value);
                
            }

            items = from n in storage where (n.dirty == true) && (n.registerType == mapFields.regType.holding) && n.parameterType == paramType.float_t select n;

            foreach (mapFields item in items)
            {
                HoldingWrite.addRegister((ushort)(item.baseRegister));
                HoldingWrite.addRegister((ushort)(item.baseRegister + 1));
                byte[] all = BitConverter.GetBytes(float.Parse(item.value));
                
                //if t1 or t2 is slected this needs to be flipped
                holdingRegistersWrite[item.baseRegister] = BitConverter.ToUInt16(all, 2);
                holdingRegistersWrite[item.baseRegister + 1] = BitConverter.ToUInt16(all, 0);
            }

            //this for float invert types

            items = from n in storage where (n.dirty == true) && (n.registerType == mapFields.regType.holding) && n.parameterType == paramType.float_i_t select n;

            foreach (mapFields item in items)
            {
                HoldingWrite.addRegister((ushort)(item.baseRegister));
                HoldingWrite.addRegister((ushort)(item.baseRegister + 1));
                byte[] all = BitConverter.GetBytes(float.Parse(item.value));

                //if t1 or t2 is slected this needs to be flipped
                holdingRegistersWrite[item.baseRegister] = BitConverter.ToUInt16(all, 0);
                holdingRegistersWrite[item.baseRegister + 1] = BitConverter.ToUInt16(all, 2);
            }

            //handles long types


            items = from n in storage where (n.dirty == true) && (n.registerType == mapFields.regType.holding) && n.parameterType == paramType.long_t select n;

            foreach (mapFields item in items)
            {
                HoldingWrite.addRegister((ushort)(item.baseRegister));
                HoldingWrite.addRegister((ushort)(item.baseRegister + 1));
                byte[] all = BitConverter.GetBytes((Int32) float.Parse(item.value));

                //if t1 or t2 is slected this needs to be flipped
                holdingRegistersWrite[item.baseRegister] = BitConverter.ToUInt16(all, 0);
                holdingRegistersWrite[item.baseRegister + 1] = BitConverter.ToUInt16(all, 2);
            }


            //does the solving and writing
            HoldingWrite.solve();
            
            foreach (pair item in HoldingWrite.getSolution())
            {
                lock (master)
                {

                    try
                    {
                         master.WriteMultipleRegisters(slaveID, (ushort)(item.baseRegister - plcOffset), holdingRegistersWrite.Skip(item.baseRegister).Take(item.range).ToArray());
                    }
                    catch (Exception err)
                    {   
                        throw (new Exception(String.Format("Starting Register: {0} Number of Items: {1} \r\n {2}",item.baseRegister,item.range,err.Message)));
                    }

                }
            }

            for (int i = 0; i < storage.Count; i++)
            {
                storage[i].dirty = false;
            }
        }


        private void processInputRegister()
        {
            populateOptimize(30000, optInputRegRead, mapFields.regType.input);
        
        }

        private void processHoldingRegister()
        {
            populateOptimize(40000, optHoldRegRead, mapFields.regType.holding);
        }
        /// <summary>
        /// Function will populate optimize range class 
        /// </summary>
        /// <param name="offset">offset is 30000 or 40000 depending on input or
        /// holding register</param>
        /// <param name="regType">passes in the optimizerange class for ether
        /// input or holding register</param>
        private void populateOptimize(int offset, optimizeRange regType, mapFields.regType type)
        {
            foreach (mapFields item in storage )
            {
                if (item.registerType == type)
                {
                    if (item.parameterType == paramType.word_t)
                    {
                        regType.addRegister((ushort)(item.register - offset ));
                    }
                    else if (item.parameterType == paramType.float_t)
                    {
                        regType.addRegister((ushort)(item.register - offset));
                        regType.addRegister((ushort)(item.register - offset + 1));
                    }
                    else if (item.parameterType == paramType.float_i_t)
                    {
                        regType.addRegister((ushort)(item.register - offset));
                        regType.addRegister((ushort)(item.register - offset + 1));
                    }
                    else if (item.parameterType == paramType.long_t)
                    {
                        regType.addRegister((ushort)(item.register - offset));
                        regType.addRegister((ushort)(item.register - offset + 1));
                    }
                }
            }
        }
        /// <summary>
        /// need serial modbus port created and initialized and slave id set
        /// Will go and actually read the data from modbus and parse the results
        /// into the database
        /// </summary>
        /// <returns>return the amount of time taken for command in ms</returns>
        public long getDataFromDevice()
        {
            if (master == null || slaveID == 0)
            {
                throw new Exception("Set port handle and slave ID");
            }
            


            master.Transport.ReadTimeout = 1000;

            // time how long this operation takes 
            Stopwatch timer = new Stopwatch();
            timer.Reset();
            timer.Start();

            getInputRegisterValues();
            getHoldingRegisterValues();
            parseResults();
            timer.Stop();

            return timer.ElapsedMilliseconds;
        }
        /// <summary>
        /// function will fetch input register values
        /// </summary>
        private void getInputRegisterValues()
        {   
            foreach (pair item in optInputRegRead.getSolution())
            {
                try
                {
                    ushort[] input;
                    lock (master)
                    {
                         input = master.ReadInputRegisters(slaveID, (ushort)(item.baseRegister - plcOffset), (ushort)item.range);
                    }
                    //this sleep is for redeye's 5ms time delay
                    System.Threading.Thread.Sleep(10);
                    
                    
                    for (int i = 0; i < input.Length; i++)
                    {
                        inputRegistersRead[item.baseRegister+i] = input[i];
                    }
                }
                catch (Exception err)
                {
                    Exception extraData = new Exception(err.Message + " InputRegister BaseRegister: " +item.baseRegister.ToString() + " Range: " + item.range.ToString());
                    throw extraData;
                    
                }

            }
        }
        

        /// <summary>
        /// function will go out and get holding register values
        /// </summary>
        private long  getHoldingRegisterValues()
        {
            Stopwatch test = new Stopwatch();
            test.Reset();
            test.Start();
            foreach (pair item in optHoldRegRead.getSolution())
            {

                try
                {
                    ushort[] input;
                    lock (master)
                    {
                        input = master.ReadHoldingRegisters(slaveID, (ushort)(item.baseRegister - plcOffset), (ushort)item.range);
                    }
                    for (int i = 0; i < input.Length; i++)
                    {
                        holdingRegistersRead[item.baseRegister + i] = input[i];
                    }
                }
                catch (Exception err)
                {
                    Exception extraData = new Exception(err.Message + " Holding BaseRegister: " + item.baseRegister.ToString() + " Range: " + item.range.ToString());
                    throw extraData;
                    
                }
                // time delay to make sure slave device has time to respond
                System.Threading.Thread.Sleep(30);
                
                

            }
            test.Stop();
            return test.ElapsedMilliseconds;
        }

        private void parseResults()
        {
            for (int i=0; i<storage.Count; i++)
            {
                if (storage[i].registerType == mapFields.regType.holding && storage[i].parameterType == paramType.float_t)
                {
                    //storage[i].register = 12123;
                    storage[i].setValue(ModbusUtility.GetSingle(holdingRegistersRead[storage[i].baseRegister], holdingRegistersRead[storage[i].baseRegister + 1]));
                }
                else if (storage[i].registerType == mapFields.regType.input && storage[i].parameterType == paramType.float_t)
                {
                    storage[i].setValue(ModbusUtility.GetSingle(inputRegistersRead[storage[i].baseRegister], inputRegistersRead[storage[i].baseRegister+ 1]));
                } else if (storage[i].registerType == mapFields.regType.holding && storage[i].parameterType == paramType.word_t)
                {
                    //storage[i].register = 12123;
                    storage[i].setValue(holdingRegistersRead[storage[i].baseRegister]); ;
                }
                else if (storage[i].registerType == mapFields.regType.input && storage[i].parameterType == paramType.word_t)
                {
                    //storage[i].register = 12123;
                    storage[i].setValue(inputRegistersRead[storage[i].register - 30000]);
                }
                else if (storage[i].registerType == mapFields.regType.holding && storage[i].parameterType == paramType.float_i_t)
                {
                    //storage[i].register = 12123;
                    storage[i].setValue(ModbusUtility.GetSingle(holdingRegistersRead[storage[i].baseRegister + 1],holdingRegistersRead[storage[i].baseRegister]));
                }
                else if (storage[i].registerType == mapFields.regType.input && storage[i].parameterType == paramType.float_i_t)
                {
                    storage[i].setValue(ModbusUtility.GetSingle( inputRegistersRead[storage[i].baseRegister + 1],inputRegistersRead[storage[i].baseRegister]));
                }
                else if (storage[i].registerType == mapFields.regType.input && storage[i].parameterType == paramType.long_t)
                {
                    byte[] temp = new byte[4];
                    temp[0] = BitConverter.GetBytes(inputRegistersRead[storage[i].baseRegister + 1])[0];
                    temp[1] = BitConverter.GetBytes(inputRegistersRead[storage[i].baseRegister + 1])[1];
                    temp[2] = BitConverter.GetBytes(inputRegistersRead[storage[i].baseRegister])[0];
                    temp[3] = BitConverter.GetBytes(inputRegistersRead[storage[i].baseRegister])[1]; ;
                    storage[i].setValue((float) BitConverter.ToInt32(temp,0));
                }
            }
        }
    }
}
